package fr.vvins.core.biz.search;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.googlecode.ehcache.annotations.Cacheable;

import fr.vvins.core.biz.search.dto.SearchFacetResultDto;
import fr.vvins.core.biz.search.dto.SearchFacetScoreDto;
import fr.vvins.core.biz.search.dto.SearchResultDto;
import fr.vvins.core.biz.search.dto.SuggestResultDto;
import fr.vvins.core.common.TechnicalException;
import fr.vvins.core.common.dto.DtoException;
import fr.vvins.core.common.dto.DtoFactory;
import fr.vvins.core.common.dto.EntityDtoConverter;
import fr.vvins.core.dao.Entity;
import fr.vvins.core.search.SearchEngineException;
import fr.vvins.core.search.query.SearchFacetResult;
import fr.vvins.core.search.query.SearchResult;
import fr.vvins.core.search.tools.SearchTools;

public class SearchManagerImpl implements SearchManager {

	@Autowired
	private SearchTools tools;

	@Autowired
	private DtoFactory dtoFactory;
	
	@Autowired
	private EntityDtoConverter converter;
	
	private Integer maxSuggestResults;

	private Integer maxResults;

	@Override
	@Cacheable(cacheName="search")
	@Transactional
	public SearchResultDto find(String word) throws SearchException, TechnicalException {
		try {
			SearchResult result = getTools().searchAll(word, 0, getMaxResults());
			return buildResult(result);
		} catch (SearchEngineException e) {
			throw new SearchException(SearchException.SEARCH_ERROR,e.getMessage());
		}
	}
	
	@Override
	@Cacheable(cacheName="search")
	@Transactional
	public SearchFacetResultDto find(List<String> facets, Map<String,String> facetsLimit, Map<String,Map<String,Long>> facetsRange) throws SearchException, TechnicalException {
		return find(null, facets, facetsLimit, facetsRange);
	}
	
	@Override
	@Cacheable(cacheName="search")
	@Transactional
	public SearchResultDto findByQuery(String query) throws SearchException, TechnicalException {
		try {
			return buildResult(getTools().search(query));
		} catch (SearchEngineException e) {
			throw new SearchException(SearchException.SEARCH_ERROR,e);
		}
	}

	@Override
	@Cacheable(cacheName="search")
	@Transactional
	public SearchFacetResultDto find(String word, List<String> facets, Map<String,String> facetsLimit, Map<String,Map<String,Long>> facetsRange) throws SearchException, TechnicalException {
		try {
			SearchFacetResult result = getTools().searchAllByFacet(word, facets, facetsLimit, facetsRange, 0, getMaxResults());
			return buildFacetResult(result);
		} catch (SearchEngineException e) {
			throw new SearchException(SearchException.SEARCH_ERROR,e.getMessage());
		}
	}
	

	@Override
	@Cacheable(cacheName="search")
	@Transactional
	public SearchResultDto findEntity(String entityType, String word) throws SearchException, TechnicalException {
		try {
			SearchResult result = getTools().searchEntity(entityType, word, 0, getMaxResults());
			return buildResult(result);
		} catch (SearchEngineException e) {
			throw new SearchException(SearchException.SEARCH_ERROR,e.getMessage());
		}
	}
	
	@Override
	@Cacheable(cacheName="search")
	@Transactional
	public SearchFacetResultDto findEntity(String entity, List<String> facets, Map<String,String> facetsLimit, Map<String,Map<String,Long>> facetsRange) throws SearchException, TechnicalException {
		return findEntity(entity, null, facets, facetsLimit, facetsRange);
	}


	@Override
	@Transactional
	@Cacheable(cacheName="search")
	public SearchFacetResultDto findEntity(String entityType, String word, List<String> facets, Map<String,String> facetsLimit, Map<String,Map<String,Long>> facetsRange) throws SearchException, TechnicalException {
		try {
			SearchFacetResult result = getTools().searchEntityByFacet(entityType, word, facets, facetsLimit, facetsRange, 0, getMaxResults());
			return buildFacetResult(result);
		} catch (SearchEngineException e) {
			throw new SearchException(SearchException.SEARCH_ERROR,e.getMessage());
		}
	}


	@Override
	@Transactional
	@Cacheable(cacheName="search")
	public SuggestResultDto suggest(String word) throws SearchException, TechnicalException {
		try {
			return new SuggestResultDto(getTools().suggest(word, getMaxSuggestResults()));
		} catch (SearchEngineException e) {
			throw new SearchException(SearchException.SEARCH_ERROR,e.getMessage());
		}
	}


	@Override
	@Transactional
	@Cacheable(cacheName="search")
	public SuggestResultDto suggestEntity(String entity, String word) throws SearchException, TechnicalException {
		try {
			return new SuggestResultDto(getTools().suggestEntity(entity, word, getMaxSuggestResults()));
		} catch (SearchEngineException e) {
			throw new SearchException(SearchException.SEARCH_ERROR,e.getMessage());
		}
	}

	private SearchResultDto buildResult(SearchResult result) throws DtoException{
		SearchResultDto dto = new SearchResultDto();
		BeanUtils.copyProperties(result, dto, new String[]{"results"});
		if(result.getResults() != null){
			for (Entity entity : result.getResults()){
				dto.addResult(converter.convertEntityToDtoInfo(entity));
			}
		}
		return dto;
	}
	
	private SearchFacetResultDto buildFacetResult(SearchFacetResult result) throws DtoException{
		SearchFacetResultDto dto = new SearchFacetResultDto();
		BeanUtils.copyProperties(result, dto, new String[]{"results", "facetScores"});
		if(result.getResults() != null){
			for (Entity entity : result.getResults()){
				dto.addResult(converter.convertEntityToDtoInfo(entity));
			}
		}
		List<SearchFacetScoreDto> scoresDto = new ArrayList<SearchFacetScoreDto>();
		for(String name : result.getFacetScore().keySet()){
			SearchFacetScoreDto scoreDto = new SearchFacetScoreDto();
			scoreDto.setName(name);
			scoreDto.setScores(result.getFacetScore().get(name));
			scoresDto.add(scoreDto);
		}
		dto.setFacetsScores(scoresDto);
		return dto;
	}

	public SearchTools getTools() {
		return tools;
	}

	public void setTools(SearchTools tools) {
		this.tools = tools;
	}

	public DtoFactory getDtoFactory() {
		return dtoFactory;
	}

	public void setDtoFactory(DtoFactory dtoFactory) {
		this.dtoFactory = dtoFactory;
	}

	public EntityDtoConverter getConverter() {
		return converter;
	}

	public void setConverter(EntityDtoConverter converter) {
		this.converter = converter;
	}

	public Integer getMaxSuggestResults() {
		return maxSuggestResults;
	}

	public void setMaxSuggestResults(Integer maxSuggestResults) {
		this.maxSuggestResults = maxSuggestResults;
	}

	public Integer getMaxResults() {
		return maxResults;
	}

	public void setMaxResults(Integer maxResults) {
		this.maxResults = maxResults;
	}
	

}
